#include "N1Unit.h"
#include "N1MLT.h"
#include "N1Lib.h"

#ifdef N1UNIT

static struct N1UnitListItem* gN1UnitListFirstItem;
static struct N1UnitListItem* gN1UnitListLastItem;

/*****************************************************
Format of performance results file:
<Function name> <call count> <time in seconds>
N1TestFunctonXdr 1000 1.245
*****************************************************/

static const char* gPerformaceResultFile;

char N1UnitAddFunction(N1UnitListItem *p)
{
    if (!gN1UnitListFirstItem)
    {
        gN1UnitListFirstItem = gN1UnitListLastItem = p;
        return 0;
    }
    gN1UnitListLastItem->next = p;
    gN1UnitListLastItem = p;

    return 0;
}

static bool _N1IsPerformanceTest(N1UnitListItem *p)
{
    return p->perf;
}

static bool _N1IsSkipTest(N1UnitListItem *p)
{
    return p->skip;
}


uint32 N1UnitRunTests(uint32 count, const char* mask)
{
    N1UnitListItem* ptr = gN1UnitListFirstItem;
    int ret_ok, ret_error, ret_exception, ret_expected, ret_expected_success;
    int ret_skip;
    const char ok[] = "ok";
    const char error[] = "error";
    const char exception[] = "exception";
    const char expected[] = "expected";
    char       exception_msg[128];
    const char* result;
    const char* error_msg;
    int         error_num;

    ret_ok = ret_error = ret_exception = 
    ret_expected = ret_expected_success = ret_skip = 0;

    printf("Test passes: %u\n", count);

    for(uint32 i=0;i<count;i++)
    {
        ptr = gN1UnitListFirstItem;

        while(ptr)
        {
            result = ok;
            error_msg = NULL;
            error_num = 0;

            if (!mask && (_N1IsSkipTest(ptr) || _N1IsPerformanceTest(ptr)))
            {
                ret_skip++;
                printf("%s - skipped\n", ptr->name);
                ptr = ptr->next;	
                continue;
            }


            try 
            {
                if(mask)
                {
                    if(strstr(ptr->name, mask))
                    {
                        ptr->func();
                    }
                } 
                else
                {
                    ptr->func();
                }
            }
            catch(N1Exception &e)
            {
                strcpy(exception_msg, *e.getMessage());
                ret_error++;
                result = error_msg = exception_msg;
            }
            catch(N1UnitError &e)
            {
                if (ptr->error==e.m_nError && e.m_nError) //expected error
                {
                    ret_expected++;
                    error_num = ptr->error;
                    result = expected;
                } 
                else 
                {
                    ret_error++;
                    error_num = e.m_nError;
                    if (e.m_sError)
                    {
                        error_msg = e.m_sError;
                        result = e.m_sError;
                    }
                    else
                        result = error;
                }
            }
            catch(...)
            {
                if (ptr->error==N1Unit_Exception) //expected exception
                {
                    ret_expected++;
                    result = expected;
                } 
                else 
                {
                    ret_exception++;
                    result = exception;
                }
            }
           
            if (result==ok)
            {
                if (ptr->error!=N1Unit_Success) //unexpected success
                {
                    ret_expected_success++;
                }
                else
                {
                    ret_ok++;
                }
            }
                        
            printf("%s - %s (%u)\n", ptr->name, result, error_num);

            ptr = ptr->next;	
        }
    }

    printf("Success: %u\n", ret_ok);
    printf("Error: %u\n", ret_error);
    printf("Exception: %u\n", ret_exception);
    printf("Expected: %u\n", ret_expected);
    printf("Expected success: %u\n", ret_expected_success);
    printf("Skipped: %u\n", ret_skip);

#ifdef N1MLT
    N1MLT_PrintResults(stderr);
#endif

    return 0;
}

/*****************************************************/
/*****************************************************/
/*****************************************************/
#ifdef N1UNIT_SELFTEST

N1TEST(N1TestFuncOK)
{
    N1RET_OK;
}

N1TEST(N1TestFuncErrorNum1)
{
    N1RET_ERROR(1);
}

N1TEST_ERROR(N1TestFuncErrorExpectedNum2,2)
{
    N1RET_ERROR(2);
}

N1TEST_ERROR(N1TestFuncErrorExpectedNum3,3)
{
    N1RET_OK;
}

N1TEST(N1TestFuncErrorStr1)
{
    N1RET_ERROR("Return error str 1!");
}

N1TEST_ERROR(N1TestFuncErrorExpectedStr2,3)
{
    N1RET_ERROR("Ret error str 2, expected error 3!");
}

N1TEST(N1TestFuncException1)
{
    throw 1;
}

N1TEST_EXCEPTION(N1TestFuncExceptionExpected2)
{
    throw 2;
}

N1TEST(N1TestN1Exception)
{
    N1ThrowException("Test");
}


N1TEST_EXCEPTION(N1TestFuncExceptionExpected3)
{
    N1RET_OK;
}

N1TEST(N1TestFuncAccessViolation1)
{
    *(int*)(NULL) = 1;
}


#endif //N1UNIT_SELFTEST

#endif //N1UNIT
